package com.gitee.wsl.jvm.io.resources

import java.io.IOException
import java.net.URI
import java.nio.file.FileSystem
import java.nio.file.FileSystemAlreadyExistsException
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.ProviderNotFoundException
import java.util.function.Consumer
import java.util.stream.Collectors

class JarFileSystemProvider : FileSystemProvider {
    private val openFileSystems: MutableMap<String, FileSystem> = HashMap()

    @Throws(IOException::class)
    override fun toPath(uri: URI): Path {
        try {
            require("jar" == uri.scheme) { "JarFileSystemProvider only supports uri's starting with jar:" }

            val fileSystemName: String = uri.toString().substringBeforeLast( "!")
            val path: String = uri.toString().substringAfterLast( "!")

            val fs = getFileSystem(fileSystemName)
            return fs!!.getPath(path)
        } catch (e: ProviderNotFoundException) {
            throw ProviderNotFoundException("Provider not found for URI $uri")
        }
    }

    @Throws(IOException::class)
    private fun getFileSystem(fileSystemName: String): FileSystem? {
        if (!openFileSystems.containsKey(fileSystemName)) {
            if (!fileSystemName.contains("!")) {
                try {
                    openFileSystems[fileSystemName] = FileSystems.newFileSystem(
                        URI.create(fileSystemName),
                        emptyMap<String, Any>(),
                        null
                    )
                } catch (e: FileSystemAlreadyExistsException) {
                    openFileSystems[fileSystemName] =
                        FileSystems.getFileSystem(URI.create(fileSystemName))
                }
            } else {
                val parent = getFileSystem(fileSystemName.substringBeforeLast("!"))
                val path = parent!!.getPath(fileSystemName.substringAfterLast("!"))
                openFileSystems[fileSystemName] =
                    FileSystems.newFileSystem(path, ClassLoader.getSystemClassLoader())
            }
        }
        return openFileSystems[fileSystemName]
    }

    @Throws(IOException::class)
    override fun close() {
        // Nested filesystems need to be closed in order
        val fileSystemNames: List<String> = ArrayList(openFileSystems.keys)

        for (i in 3 downTo 0) {
            val finalI = i
            val fileSystemsToClose = fileSystemNames.stream()
                .filter { fsName: String ->
                    fsName.chars().filter { ch: Int -> ch == '!'.code }
                        .count() == finalI.toLong()
                }
                .collect(Collectors.toList())

            fileSystemsToClose.forEach(Consumer { fileSystemName: String ->
                this.close(
                    fileSystemName
                )
            })
            fileSystemsToClose.forEach(Consumer { o: String -> openFileSystems.remove(o) })
        }
    }

    private fun close(fileSystemName: String) {
        try {
            openFileSystems.remove(fileSystemName)!!.close()
        } catch (e: IOException) {
            // nothing more we can do
        }
    }
}
